using System;
using System.Collections.Generic;
using System.Text;
using System.Diagnostics;

namespace GoodStuff
{
    // a goodvariable:
    // 1. has a name
    // 2. has a displayname that describes the function of the variable to a user interacting with the variable
    // 3. optionally has a help string that provides more infomation to a user about the function of the variable
    // 5. provides a public event indicating that the value of the variable has changed
    // 6. provides an object instance to lock on for thread synchronization

    public class GoodVariableBase : IGoodVariable
    {
        protected string _instanceName;
        public string InstanceName
        {
            get { return _instanceName; }
            set { _instanceName = value; }
        }

        Dictionary<string, IGoodVariable> _properties;
        public Dictionary<string, IGoodVariable> Properties
        {
            get { return _properties; }
            set { _properties = value; }
        }


        public GoodVariableBase()
        {
            _syncRoot = new object();
            _properties = new Dictionary<string, IGoodVariable>();
        }

        public event EventHandler ValueChanged;
        protected void RaiseValueChanged()
        {
            if (ValueChanged != null)
                ValueChanged(this, new EventArgs());
        }

        protected object _syncRoot;
        public object SyncRoot
        {
            get { return _syncRoot; }
        }
    }

    // 8 & 9. provides type specific get and set operators

    public class GoodVariable<T> : GoodVariableBase
    {
        protected T _value;
        virtual public T Value 
        {   
            get 
            { 
                return _value; 
            } 
            set 
            {
                lock (_syncRoot)
                {
                    if (_validator != null)
                        _value = _validator(this, value);
                    else
                        _value = value;
                }
                RaiseValueChanged();
            }
        }

        protected GoodValidator<T> _validator;
        public GoodValidator<T> Validator
        {
            get { return _validator; }
            set 
            {
                Trace.WriteLine("Validator set for GoodVariable " + _instanceName);
                _validator = value;
            }
        }
    }

    public interface IGoodConvertibleVariable : IGoodVariable, IConvertible
    {
        void Set(IConvertible   v); 
    }

    public class GoodConvertibleVariable<T> : GoodVariable<T>, IGoodConvertibleVariable where T: IConvertible
    {
        #region IConvertible Members

        public TypeCode GetTypeCode() { return _value.GetTypeCode(); }
        public bool     ToBoolean(IFormatProvider provider) { return _value.ToBoolean(provider); }
        public byte     ToByte(IFormatProvider provider) { return _value.ToByte(provider); }
        public char     ToChar(IFormatProvider provider) { return _value.ToChar(provider); }
        public DateTime ToDateTime(IFormatProvider provider) { return _value.ToDateTime(provider); }
        public decimal  ToDecimal(IFormatProvider provider) { return _value.ToDecimal(provider); }
        public double   ToDouble(IFormatProvider provider) { return _value.ToDouble(provider); }
        public short    ToInt16(IFormatProvider provider) { return _value.ToInt16(provider); }
        public int      ToInt32(IFormatProvider provider) { return _value.ToInt32(provider); }
        public long     ToInt64(IFormatProvider provider) { return _value.ToInt64(provider); }
        public sbyte    ToSByte(IFormatProvider provider) { return _value.ToSByte(provider); }
        public float    ToSingle(IFormatProvider provider) { return _value.ToSingle(provider); }
        public string   ToString(IFormatProvider provider) { return _value.ToString(provider); }
        public object   ToType(Type conversionType, IFormatProvider provider) { return _value.ToType(conversionType, provider); }
        public ushort   ToUInt16(IFormatProvider provider) { return _value.ToUInt16(provider); }
        public uint     ToUInt32(IFormatProvider provider) { return _value.ToUInt32(provider); }
        public ulong    ToUInt64(IFormatProvider provider) { return _value.ToUInt64(provider); }

        #endregion

        // type cast operators

        public static explicit operator bool(GoodConvertibleVariable<T> v) { return v.Value.ToBoolean(null); }
        public static explicit operator byte(GoodConvertibleVariable<T> v) { return v.Value.ToByte(null); }
        public static explicit operator char(GoodConvertibleVariable<T> v) { return v.Value.ToChar(null); }
        public static explicit operator DateTime(GoodConvertibleVariable<T> v) { return v.Value.ToDateTime(null); }
        public static explicit operator decimal(GoodConvertibleVariable<T> v) { return v.Value.ToDecimal(null); }
        public static explicit operator double(GoodConvertibleVariable<T> v) { return v.Value.ToDouble(null); }
        public static explicit operator short(GoodConvertibleVariable<T> v) { return v.Value.ToInt16(null); }
        public static explicit operator int(GoodConvertibleVariable<T> v) { return v.Value.ToInt32(null); }
        public static explicit operator long(GoodConvertibleVariable<T> v) { return v.Value.ToInt64(null); }
        public static explicit operator sbyte(GoodConvertibleVariable<T> v) { return v.Value.ToSByte(null); }
        public static explicit operator float(GoodConvertibleVariable<T> v) { return v.Value.ToSingle(null); }
        public static explicit operator string(GoodConvertibleVariable<T> v) { return v.Value.ToString(null); }
        public static explicit operator ushort(GoodConvertibleVariable<T> v) { return v.Value.ToUInt16(null); }
        public static explicit operator uint(GoodConvertibleVariable<T> v) { return v.Value.ToUInt32(null); }
        public static explicit operator ulong(GoodConvertibleVariable<T> v) { return v.Value.ToUInt64(null); }

        // TODO: handle exceptions in all of these

        public void Set(IConvertible v) 
        { 
            Value = (T)(v as IConvertible).ToType(typeof(T), null);
        }
    }

    public delegate T GoodValidator<T>(GoodVariable<T> v, T newValue);

    public class GoodValidatorDecimalMinMaxMod<T> where T: IConvertible, IComparable
    {
        public int Min;
        public int Max;
        public int Mod;

        public T Validate(GoodVariable<T> v, T newValue)
        {
            GoodConvertibleVariable<T> gnv = v as GoodConvertibleVariable<T>;
            int temp = newValue.ToInt32(null);

            if ((Mod != 0) && (temp % Mod != 0))
            {
                temp = (temp > (int)gnv) ?
                    temp + Mod - temp % Mod :
                    temp - temp % Mod;
            }

            temp = Math.Min(Max, temp);
            temp = Math.Max(Min, temp);

            return (T)newValue.ToType(typeof(T),null);
        }
    }
}